﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.IO;
using System.Linq;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;

namespace JMacia.Gaming.d20.Core.Traits
{
    [Serializable()]
    public partial class Effect
    {
        #region Static
        #region Private Static Fields
        private static EffectLibrary s_library = new EffectLibrary();
        #endregion

        #region Public Static Properties
        public static EffectLibrary Library
        {
            get { return s_library; }
        }
        #endregion

        #region Public Static Methods
        public static bool LoadLibrary(string filePath)
        {
            try
            {
                using (FileStream fs = new FileStream(filePath,
                    FileMode.Open, FileAccess.Read, FileShare.Read))
                {
                    BinaryFormatter b = new BinaryFormatter();
                    object o = b.Deserialize(fs);
                    s_library = o as EffectLibrary;
                    return s_library != null;
                }
            }
            catch { return false; }
        }
        public static bool SaveLibrary(string filePath)
        {
            return SaveLibrary(filePath, null);
        }
        public static bool SaveLibrary(string filePath,
            DuplicateEffectNameHandler duplicateHandler)
        {
            if (duplicateHandler != null)
            {
                s_library.Consolidate(duplicateHandler);
            }
            try
            {
                using (FileStream fs = new FileStream(filePath,
                    FileMode.Create, FileAccess.Write, FileShare.None))
                {
                    BinaryFormatter b = new BinaryFormatter();
                    b.Serialize(fs, s_library);
                    return true;
                }
            }
            catch { return false; }
        }
        #endregion
        #endregion

        #region Instance
        #region Private Fields
        private string _name;
        private string _shortDescription;
        private string _fullDescription;
        private Modifiers _providedModifiers = new Modifiers();
        #endregion

        #region Constructors
        public Effect()
        {
            s_library.Add(this);
            _providedModifiers.CollectionChanged += new NotifyCollectionChangedEventHandler(ModifiersChanged);
        }
        #endregion

        #region Public Properties
        public string Name
        {
            get { return _name; }
            set { _name = value; }
        }
        public string ShortDescription
        {
            get { return _shortDescription; }
            set { _shortDescription = value; }
        }
        public string FullDescription
        {
            get { return _fullDescription; }
            set { _fullDescription = value; }
        }
        public Modifiers ProvidedModifiers
        {
            get { return _providedModifiers; }
        }
        #endregion

        #region Public Methods
        public Effect Clone()
        {
            Effect e = new Effect();

            e._providedModifiers = this._providedModifiers.Clone();

            return e;
        }

        #region Overridden Methods
        public override bool Equals(object obj)
        {
            Effect b = obj as Effect;
            if (b == null) { return false; }
            return this == b;
        }
        public override int GetHashCode()
        {
            return _name.GetHashCode() ^ 
                _shortDescription.GetHashCode() ^ 
                _fullDescription.GetHashCode();
        }
        public override string ToString()
        {
            return _name;
        }
        #endregion
        #endregion

        #region Private Methods
        private void ModifiersChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            if (e.OldItems != null)
            {
                foreach (Modifier m in e.OldItems)
                {
                    m.Source = null;
                }
            }

            if (e.NewItems != null)
            {
                foreach (Modifier m in e.NewItems)
                {
                    if (m.Source != null && m.Source != this)
                    {
                        m.Source._providedModifiers.Remove(m);
                    }
                    m.Source = this;
                }
            }
        }
        #endregion

        #region Operators
        public static bool operator ==(Effect one, Effect two)
        {
            if (object.ReferenceEquals(one, null))
            {
                return object.ReferenceEquals(two, null);
            }
            if (object.ReferenceEquals(two, null)) { return false; }

            return one._name == two._name &&
                one._shortDescription == two._shortDescription &&
                one._fullDescription == two._fullDescription;
        }
        public static bool operator !=(Effect one, Effect two)
        {
            return !(one == two);
        }
        #endregion
        #endregion

        #region Subclasses
        public class EffectLibrary : ObservableCollection<Effect>
        {
            public List<Effect> this[string name]
            {
                get
                {
                    List<Effect> list = new List<Effect>();
                    foreach (Effect def in this)
                    {
                        if (string.Equals(name, def._name,
                            StringComparison.CurrentCultureIgnoreCase))
                        {
                            list.Add(def);
                        }
                    }
                    return list;
                }
            }

            public void Consolidate(DuplicateEffectNameHandler duplicateHandler)
            {
                if (duplicateHandler == null) { return; }

                List<Effect> copy = new List<Effect>(this);

                foreach (Effect eff in copy)
                {
                    List<Effect> thisName = this[eff.Name];
                    if (thisName.Count > 1)
                    {
                        List<Effect> newList = new List<Effect>(thisName);
                        duplicateHandler(newList);
                        foreach (Effect old in thisName)
                        {
                            this.Remove(old);
                        }
                        foreach (Effect newEff in newList)
                        {
                            this.Add(newEff);
                        }
                    }
                }
            }
        }
        #endregion
    }

    public delegate void DuplicateEffectNameHandler(List<Effect> sameName);

    public class EffectNameComparer : IEqualityComparer<Effect>, IComparer<Effect>
    {
        #region IEqualityComparer<Effect> Members

        public bool Equals(Effect x, Effect y)
        {
            if (object.ReferenceEquals(x, null))
            {
                return object.ReferenceEquals(y, null);
            }
            if (object.ReferenceEquals(y, null)) { return false; }

            return string.Equals(x.Name, y.Name,
                StringComparison.CurrentCultureIgnoreCase);
        }

        public int GetHashCode(Effect obj)
        {
            if (obj == null) { return 0; }
            return obj.Name.GetHashCode();
        }

        #endregion

        #region IComparer<Effect> Members

        public int Compare(Effect x, Effect y)
        {
            if (object.ReferenceEquals(x, null))
            {
                if (object.ReferenceEquals(y, null))
                {
                    return 0;
                }
                else
                {
                    return -1;
                }
            }
            else if (object.ReferenceEquals(y, null))
            {
                return 1;
            }

            return string.Compare(x.Name, y.Name,
                StringComparison.CurrentCultureIgnoreCase);
        }

        #endregion
    }

}
